/*
 * condition.h
 *
 *  Created on: 2008-12-25
 *      Author: fanliangliang
 */

#ifndef WIN32_CONDITION_H_
#define WIN32_CONDITION_H_

#include "intrusive_ptr.h"
#include "bits/lock.h"
#include <vector>
#include <climits>

BEGIN_NAMESPACE_TOXIC

inline long InterlockedReadAcquire(long volatile* x) {
	return InterlockedCompareExchange(x, 0, 0);
}

inline void InterlockedWriteRelease(long volatile* x, long value) {
	InterlockedExchange(x, value);
}


class HandleManager {
	CLASS_UNCOPYABLE(HandleManager)
public:
    explicit HandleManager(HANDLE handle)
    :_handle(handle) {

    }

    HandleManager()
    :_handle(0) {

    }

    ~HandleManager() {
        cleanup();
    }

    HandleManager& operator=(HANDLE handle) {
        cleanup();
        _handle = handle;
        return *this;
    }

    operator HANDLE() const {
        return _handle;
    }

    bool operator!() const {
        return !_handle;
    }

    HANDLE duplicate() const {
        HANDLE currentProcess = GetCurrentProcess();
        HANDLE handle = 0;
        ::DuplicateHandle(currentProcess, _handle, currentProcess, &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
        return handle;
    }

private:
    void cleanup() {
        if(_handle != 0 && _handle != INVALID_HANDLE_VALUE) {
            CloseHandle(_handle);
        }
    }

private:
    HANDLE _handle;
};


class ListEntry {
	CLASS_UNCOPYABLE(ListEntry)
public:
	explicit ListEntry(const HandleManager& wakeSemaphore_)
	:semaphore(CreateSemaphore(0, 0, LONG_MAX, 0))
	,wakeSemaphore(wakeSemaphore_.duplicate())
	,waiters(1)
	,notified(false)
	,references(0) {

	}

	static bool noWaiters(const intrusive_ptr<ListEntry>& entry) {
		return !InterlockedReadAcquire(&entry->waiters);
	}

	void addWaiter() {
		InterlockedIncrement(&waiters);
	}

	void removeWaiter() {
		InterlockedDecrement(&waiters);
	}

	void release(unsigned count) {
		notified = true;
		ReleaseSemaphore(semaphore, count, 0);
	}

	void releaseWaiters() {
		release(InterlockedReadAcquire(&waiters));
	}

	bool isNotified() const {
		return notified;
	}

	bool wait(unsigned long millisecond) {
		return WaitForSingleObject(wakeSemaphore, millisecond) == WAIT_OBJECT_0;
	}

private:
	HandleManager semaphore;
	HandleManager wakeSemaphore;
	long waiters;
	bool notified;
	long references;

	friend void intrusive_ptr_add_ref(ListEntry* p);
	friend void intrusive_ptr_release(ListEntry* p);
};

inline void intrusive_ptr_add_ref(ListEntry* p) {
	InterlockedIncrement(&p->references);
}

inline void intrusive_ptr_release(ListEntry* p) {
	if(!InterlockedDecrement(&p->references)) {
		delete p;
	}
}


class Condition {
	CLASS_UNCOPYABLE(Condition)
public:
	Condition();
	~Condition();

	void wait(ScopedLock& lock);
	bool wait(ScopedLock& lock, unsigned long millisecond);

	void notify();
	void notifyAll();

private:
	typedef intrusive_ptr<ListEntry> EntryPointer;
	typedef std::vector<EntryPointer> GenerationList;

	void wakeWaiters(long count);
	EntryPointer getWaitEntry();

public:
	Mutex _mutex;
	long _totalCount;
	GenerationList _generations;
	HandleManager _wakeSemaphore;
};

END_NAMESPACE_TOXIC

#endif /* WIN32_CONDITION_H_ */
